#include <CH549_DEBUG.h>
#include <CH549_sdcc.h>
#include <HID.H>
#include <USBHOST.H>
#include <compiler.h>
#include <key_conv.H>
#include <ps2.H>

__xdata __at(0x01A2) UINT8 C3_IO_size;
long binToInt(UINT32X *data) {

    switch (C3_IO_size) {
    case 1:
        return *((char *)data);
    case 2:
        return *((short *)data);
    default:
        return *((long *)data);
    }
}

/*******************************************************************************
 * Function Name  : bincpy
 * Description    : 把“源”的指定bit开始，复制指定长度到“目标”。不满位数的，用复制的最后一位填满
 * Input          : tar  ：目标
 * Output         : None
 * Return         : None
 *******************************************************************************/
__xdata __at(0x0180) PUINT8 C1_IO_src;       // 源
__xdata __at(0x0182) UINT16 C1_IO_offsetSrc; // 偏移
__xdata __at(0x0184) UINT8 C1_IO_len;        // 源的bit数
__xdata __at(0x0185) UINT8 C1_IO_padding;    // 目标的有效bit数（会被填充）
__xdata __at(0x0186) UINT8 C1_IO_srcLen;     // 目标的字节数
__xdata __at(0x0188) PUINT8 C1_L_pTar;
__xdata __at(0x018A) PUINT8 C1_L_pSrc;
__xdata __at(0x0187) UINT8 C1_L_c;
__xdata __at(0x018C) UINT8 C1_L_last;
__xdata __at(0x018D) UINT8 C1_L_i;
__xdata __at(0x018E) UINT8 C1_L_n;

void bincpy(PUINT32 tar) {
    C1_L_pTar = (PUINT8)tar;
    C1_L_pSrc = (PUINT8X)C1_IO_src;

    C1_L_pTar--;
    C1_L_pSrc += C1_IO_offsetSrc >> 3;
    C1_IO_offsetSrc &= 7;
    C1_L_c = *(C1_L_pSrc);
    if (C1_IO_offsetSrc) {
        C1_L_c >>= C1_IO_offsetSrc;
    } else {
        C1_L_pSrc--;
    }

    C1_L_last = 0;
    C1_L_i = 0;
    if (C1_IO_offsetSrc <= C1_IO_srcLen) {
        for (; C1_L_i < C1_IO_len; C1_L_i++, C1_IO_offsetSrc++) {

            if ((C1_L_i & 7) == 0) {
                *(++(C1_L_pTar)) = 0;
            }
            if ((C1_IO_offsetSrc & 7) == 0) {
                C1_L_c = *(++(C1_L_pSrc));
            }

            *C1_L_pTar >>= 1;
            if ((C1_L_c & 0x01)) {
                C1_L_last = 1;
                *C1_L_pTar |= 0x80;
            } else {
                C1_L_last = 0;
            }

            C1_L_c >>= 1;
        }
    }

    // 填充满
    while (C1_L_i < C1_IO_padding) {
        C1_L_n = C1_L_i & 7;
        if (C1_L_n == 0) {
            C1_L_pTar++;
            if (C1_L_last) {
                *C1_L_pTar = 0xFF;
            } else {
                *C1_L_pTar = 0;
            }
            C1_L_i += 8;
        } else {
            *C1_L_pTar >>= (8 - C1_L_n);
            C1_L_pTar++;
            C1_L_i += (8 - C1_L_n);
        }
    }
}

/*******************************************************************************
 * Function Name  : analyzeReportDesc
 * Description    : 分析报表描述符，结果保存到 *ppReportMap 为首的内存块
 * Input          : data  :报表描述符
 *                : offset:偏移位置
 *                : length:报表描述符长度
 *                : analyzeTag: 要分析的tag（80H:Input/90H:Output/B0H:Feature）
 * Output         : ppReportMap ： 分析结果
 * Return         : None
 *******************************************************************************/
__xdata __at(0x0190) UINT8 C2_IO_offset;
__xdata __at(0x0191) UINT8 C2_IO_analyzeTag;
__xdata __at(0x0192) UINT8 C2_L_prefix;
__xdata __at(0x0193) UINT8 C2_L_size;
__xdata __at(0x0194) UINT8 C2_L_type;
__xdata __at(0x0195) UINT8 C2_L_tag;
__xdata __at(0x0196) UINT32 C2_L_value;
__xdata __at(0x019A) UINT8 C2_L_currentUsagePage;
__xdata __at(0x019B) UINT8 C2_L_currentUsage;
__xdata __at(0x019C) UINT8 C2_L_currentUsages[5]; // 首字节是数组的长度
__xdata __at(0x01A1) UINT8 C2_L_currentBitSize;
__xdata __at(0x01A2) UINT8 C2_L_currentItemCount;
__xdata __at(0x01A3) UINT8 C2_L_currentReportId;
__xdata __at(0x01A4) UINT16 C2_L_currentBitBeginInput;
__xdata __at(0x01A6) UINT16 C2_L_currentBitBeginOutput;
__xdata __at(0x01A8) UINT16 C2_L_currentBitBeginFeature;
__xdata __at(0x01AA) long C2_L_currentUsageMin;
__xdata __at(0x01AE) long C2_L_currentUsageMax;

void analyzeReportDesc(_REPORT_MAP **ppREPORT_MAP) {
    UINT8 i;

    C2_L_prefix = Com_Buffer[C2_IO_offset++];
    C2_L_size = C2_L_prefix & 0x03;
    C2_L_type = (C2_L_prefix >> 2) & 0x03;
    C2_L_tag = C2_L_prefix & 0xfC;

    if (C2_L_size == 3)
        C2_L_size = 4; // 处理特殊情况

    C2_L_value = 0;
    for (i = 0; i < C2_L_size; i++) {
        if (C2_IO_offset >= Com_BufferLen)
            break;
        C2_L_value |= ((UINT32)Com_Buffer[C2_IO_offset++]) << (8 * i);
    }

    // 根据tag处理不同类型的项
    switch (C2_L_tag) {
    case 0x04: // Usage Page
        C2_L_currentUsagePage = C2_L_value;
        // if (C2_L_currentUsagePage == 0x01) {
        //     if (C2_L_currentUsage == 0x02) {
        //         hid_device_type = 2; // 鼠标
        //     } else if (C2_L_currentUsage == 0x06) {
        //         hid_device_type = 1; // 键盘
        //     }
        // }
        break;
    case 0x08: // Usage
        C2_L_currentUsage = C2_L_value;
        // if (C2_L_currentUsagePage == 0x01) {
        //     if (C2_L_currentUsage == 0x02) {
        //         hid_device_type = 2;
        //     } else if (C2_L_currentUsage == 0x06) {
        //         hid_device_type = 1;
        //     }
        // }

        C2_L_currentUsages[0]++;
        C2_L_currentUsages[C2_L_currentUsages[0]] = C2_L_currentUsage;
        break;
    case 0x18: // Usage Minimum
        C3_IO_size = C2_L_size;
        C2_L_currentUsageMin = binToInt(&C2_L_value);
        C2_L_currentUsages[0] = 0;
        break;
    case 0x28: // Usage Maximum
        C3_IO_size = C2_L_size;
        C2_L_currentUsageMax = binToInt(&C2_L_value);
        C2_L_currentUsages[0] = 0;
        break;
    case 0x74: // Report Size
        C2_L_currentBitSize = C2_L_value;
        break;
    case 0x94: // Report Count
        C2_L_currentItemCount = C2_L_value;
        break;
    case 0x80: // Input
        if (!(C2_L_value & 0x01) && C2_IO_analyzeTag == 0x80) {
            // Data
            if (C2_L_value & 0x02) {
                // Variable
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginInput;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    if (C2_L_currentUsages[0] == 0) {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsageMin++;
                    } else {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsages[0] > 1 ? C2_L_currentUsages[i + 1] : C2_L_currentUsages[1];
                    }
                    (*ppREPORT_MAP)++;
                    C2_L_currentBitBeginInput += C2_L_currentBitSize;
                }

            } else {
                // Array
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginInput;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    (*ppREPORT_MAP)->usage = 0;
                    (*ppREPORT_MAP)->usageMin = C2_L_currentUsageMin;
                    (*ppREPORT_MAP)->usageMax = C2_L_currentUsageMax;
                    (*ppREPORT_MAP)++;

                    C2_L_currentBitBeginInput += C2_L_currentBitSize;
                }
            }
        } else {
            C2_L_currentBitBeginInput += C2_L_currentBitSize * C2_L_currentItemCount;
        }

        C2_L_currentUsages[0] = 0;

        break;

    case 0x90: // Output
        if (!(C2_L_value & 0x01) && C2_IO_analyzeTag == 0x90) {
            // Data
            if (C2_L_value & 0x02) {
                // Variable
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginOutput;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    if (C2_L_currentUsages[0] == 0) {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsageMin++;
                    } else {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsages[0] > 1 ? C2_L_currentUsages[i + 1] : C2_L_currentUsages[1];
                    }
                    (*ppREPORT_MAP)++;
                    C2_L_currentBitBeginOutput += C2_L_currentBitSize;
                }

            } else {
                // Array
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginOutput;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    (*ppREPORT_MAP)->usage = 0;
                    (*ppREPORT_MAP)->usageMin = C2_L_currentUsageMin;
                    (*ppREPORT_MAP)->usageMax = C2_L_currentUsageMax;
                    (*ppREPORT_MAP)++;

                    C2_L_currentBitBeginOutput += C2_L_currentBitSize;
                }
            }
        } else {
            C2_L_currentBitBeginOutput += C2_L_currentBitSize * C2_L_currentItemCount;
        }

        C2_L_currentUsages[0] = 0;

        break;

    case 0xB0: // Feature
        if (!(C2_L_value & 0x01) && C2_IO_analyzeTag == 0xB0) {
            // Data
            if (C2_L_value & 0x02) {
                // Variable
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginFeature;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    if (C2_L_currentUsages[0] == 0) {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsageMin++;
                    } else {
                        (*ppREPORT_MAP)->usage = C2_L_currentUsages[0] > 1 ? C2_L_currentUsages[i + 1] : C2_L_currentUsages[1];
                    }
                    (*ppREPORT_MAP)++;
                    C2_L_currentBitBeginFeature += C2_L_currentBitSize;
                }

            } else {
                // Array
                for (i = 0; i < C2_L_currentItemCount; i++) {
                    (*ppREPORT_MAP)->type = C2_L_tag;
                    (*ppREPORT_MAP)->reportId = C2_L_currentReportId;
                    (*ppREPORT_MAP)->bitBegin = C2_L_currentBitBeginFeature;
                    (*ppREPORT_MAP)->bitSize = C2_L_currentBitSize;
                    (*ppREPORT_MAP)->usagePage = C2_L_currentUsagePage;
                    (*ppREPORT_MAP)->usage = 0;
                    (*ppREPORT_MAP)->usageMin = C2_L_currentUsageMin;
                    (*ppREPORT_MAP)->usageMax = C2_L_currentUsageMax;
                    (*ppREPORT_MAP)++;

                    C2_L_currentBitBeginFeature += C2_L_currentBitSize;
                }
            }
        } else {
            C2_L_currentBitBeginFeature += C2_L_currentBitSize * C2_L_currentItemCount;
        }

        C2_L_currentUsages[0] = 0;

        break;

    case 0x84: // Report ID
        C2_L_currentReportId = C2_L_value;
        break;
    }
}

// 打印解析的Report Map信息
void printReportMapInfo() {
    _REPORT_MAP *ptr = (_REPORT_MAP *)REPORT_MAP_AREA;
    printf("==== HID Report Map Info ====\n");
    while (ptr->type) {
        printf("Type: ");
        switch (ptr->type) {
        case 0x80:
            printf("Input");
            break;
        case 0x90:
            printf("Output");
            break;
        case 0xB0:
            printf("Feature");
            break;
        default:
            printf("Unknown");
            break;
        }

        printf(", Report ID: %d", ptr->reportId);
        printf(", Bit begin: %d", ptr->bitBegin);
        printf(", Bit Size: %d", ptr->bitSize);
        printf(", Usage Page: %02X", ptr->usagePage);
        printf(", Usage: %02X\n", ptr->usage);

        ptr++;
    }
    printf("Total: %d bytes.\n", ThisUsbDev.HIDReportSize);
    printf("============================\n");
}

// #pragma  NOAREGS
/*******************************************************************************
 * Function Name  : hid_init
 * Description    : 根据报表描述符初始化HID设备
 * Input          : DataBuf  :
 *                  nLen
 * Output         : None
 * Return         : None
 *******************************************************************************/
void hid_init() {
    // 分析Input——用来解析按键和移动信息

    UINT8 i;
    for (i = 0; i < Com_BufferLen; i++) {
        printf("%02x ", Com_Buffer[i]);
    }
    printf("\n");

    C2_IO_offset = 0;
    C2_IO_analyzeTag = 0;
    C2_L_prefix = 0;
    C2_L_size = 0;
    C2_L_type = 0;
    C2_L_tag = 0;
    C2_L_value = 0;

    C2_L_currentUsagePage = 0;
    C2_L_currentUsage = 0;
    C2_L_currentUsages[0] = 0; // 首字节是数组的长度
    C2_L_currentUsages[1] = 0; // 首字节是数组的长度
    C2_L_currentUsages[2] = 0; // 首字节是数组的长度
    C2_L_currentUsages[3] = 0; // 首字节是数组的长度
    C2_L_currentUsages[4] = 0; // 首字节是数组的长度
    C2_L_currentBitSize = 0;
    C2_L_currentItemCount = 0;
    C2_L_currentReportId = 0;
    C2_L_currentBitBeginInput = 0;
    C2_L_currentBitBeginOutput = 0;
    C2_L_currentBitBeginFeature = 0;
    C2_L_currentUsageMin = 0;
    C2_L_currentUsageMax = 0;

    C2_IO_offset = 0;
    C2_IO_analyzeTag = 0x80;
    _REPORT_MAP *pREPORT_MAP = (_REPORT_MAP *)REPORT_MAP_AREA;

    while (C2_IO_offset < Com_BufferLen) {
        analyzeReportDesc(&pREPORT_MAP);
    }
    ThisUsbDev.HIDReportSize = C2_L_currentBitBeginInput >> 3;

    // 分析Output——用来设置键盘LED
    C2_IO_offset = 0;
    C2_IO_analyzeTag = 0x90;
    while (C2_IO_offset < Com_BufferLen) {
        analyzeReportDesc(&pREPORT_MAP);
    }

    // 结尾（终止）
    pREPORT_MAP->type = 0; // none

    printReportMapInfo();
}

/*******************************************************************************
 * Function Name  : hid_parseInDataKB
 * Description    : 根据报表描述符解析报表（键盘）
 * Input          : DataBuf  :
 *                  nLen
 * Output         : None
 * Return         : None
 *******************************************************************************/
void hid_parseInDataKB(PUINT8X DataBuf, UINT8 nLen) {
    UINT8 i, idx;
    UINT32 buf;

    if (nLen) {
        for (i = 0; i < nLen; i++) {
            // 读到的InData
            printf("x%02X", DataBuf[i]);
        }
        printf("\n");
    }

    for (i = 0; i < 120; i++) {
        pPS2_KB_INFO->KeyStatusTmp[i] = 0;
    }

    pPS2_KB_INFO->KeyRepeat = 0;

    _REPORT_MAP __xdata *pREPORT_MAP = (_REPORT_MAP *)REPORT_MAP_AREA;
    while (pREPORT_MAP->type == 0x80) {
        buf = 0;
        C1_IO_src = DataBuf;
        C1_IO_offsetSrc = pREPORT_MAP->bitBegin;
        C1_IO_len = pREPORT_MAP->bitSize;
        C1_IO_padding = 16;
        C1_IO_srcLen = nLen << 3;
        bincpy(&buf);

        if (pREPORT_MAP->usagePage == 0x07) {
            // Keyboard/Keypad
            if (pREPORT_MAP->usage >= 0xE0 && pREPORT_MAP->usage <= 0xE7) {
                // 功能键
                if (buf) {
                    idx = usb_to_ps2_index_sp(pREPORT_MAP->usage & 0x0F);
                    pPS2_KB_INFO->KeyStatusTmp[idx] = 1;
                }
            } else {
                // 普通按键
                idx = usb_to_ps2_index(buf);
                if (idx) {
                    pPS2_KB_INFO->KeyStatusTmp[idx] = 1;
                    if (!pPS2_KB_INFO->KeyStatus[idx]) {
                        // 机打按键
                        pPS2_KB_INFO->KeyRepeatCnt = pPS2_KB_INFO->KeyDelay; // 机打延时计数
                        pPS2_KB_INFO->KeyRepeat = idx;
                    }
                }
            }
        }

        pREPORT_MAP++;
    }
}

/*******************************************************************************
 * Function Name  : hid_parseInDataMS
 * Description    : 根据报表描述符解析报表（鼠标）
 * Input          : DataBuf  :
 *                  nLen
 * Output         : None
 * Return         : None
 *******************************************************************************/
void hid_parseInDataMS(PUINT8X DataBuf, UINT8 nLen) {

    UINT32 buf;

    UINT8 i;
    if (nLen) {
        for (i = 0; i < nLen; i++) {
            // 读到的InData
            printf("x%02X", DataBuf[i]);
        }
        printf("\n");
    }

    _REPORT_MAP __xdata *pREPORT_MAP = (_REPORT_MAP *)REPORT_MAP_AREA;
    while (pREPORT_MAP->type == 0x80) {
        buf = 0;
        C1_IO_src = DataBuf;
        C1_IO_offsetSrc = pREPORT_MAP->bitBegin;
        C1_IO_len = pREPORT_MAP->bitSize;
        C1_IO_padding = 16;
        C1_IO_srcLen = nLen << 3;
        bincpy(&buf);

        if (pREPORT_MAP->usagePage == 0x09) {
            // Button
            pPS2_MS_INFO->KeyStatusTmp[pREPORT_MAP->usage - 1] = buf;

        } else if (pREPORT_MAP->usagePage == 0x01) {
            // 移动
            switch (pREPORT_MAP->usage) {
            case 0x30: // X
                pPS2_MS_INFO->Position[0] += (short)buf;
                break;
            case 0x31: // Y
                pPS2_MS_INFO->Position[1] -= (short)buf;
                break;
            case 0x38: // Wheel
                pPS2_MS_INFO->Position[2] -= (short)buf;
                break;
            }
        }
        pREPORT_MAP++;
    }
}

/*******************************************************************************
 * Function Name  : hid_setup
 * Description    : 通过发送指令设置HID设备
 * Input          : attrib  :要设置的属性
 *                           1:LED
 *                  value   :要设置的值
 * Output         : None
 * Return         : None
 *******************************************************************************/
void hid_setup(UINT8 attrib, UINT8 value) // 设置HID设备
{
    if (ThisUsbDev.DeviceStatus != ROOT_DEV_SUCCESS) {
        return;
    }
    switch (attrib) {
    case 1:
        // 设置LED
        // 这里省略了根据报表描述符把value转换成报表的过程。
        // 因为value本身是以“普遍”的报表格式编辑而成的。
        // 遇到不能电灯的键盘再对应吧
        setReport(value);
        break;
    }
}
